(function(){
	var Db = require('mongodb').Db,
	    Server = require('mongodb').Server,
	    ObjectID = require('mongodb').ObjectID,
	    StringUtils = require('../StringUtils.js'),
	    logFactory = require('../Log');
	
	var _ = StringUtils.format;
	var log = logFactory.create('DB');
	
	var Mongo = function(host, port, dbname){
		this.host = host;
		this.port = port;
		this.dbname = dbname;
		this.connectionCount = 1;
	};
	Mongo.prototype = {
		getDb : function(collectionName){
			var db = new Mongo.DB({parent:this, collectionName:collectionName});
			return db;
		},
		execute : function(db, task){
			var id = this.connectionCount++;
			var scope = this;
			var _client = new Db(this.dbname, new Server(this.host, this.port, {}));
			_client.open(function(err, p_client){
				if( err ) 
					log.error('[conn:${id}] - Error : ${err}', {id:scope.connectionCount, err:err});
				else
					log.info('[conn:${connectionCount}] - Opened', scope);
				var done = function(){
					log.info('[conn:${connectionCount}] - Closed', scope);
					_client.close();
				};
				_client.collection(db.collectionName, function(err, collection){
					if( err ) 
						log.error('[conn:${id}] - Error : ${err}', {id:scope.connectionCount, err:err});
					task(id, collection, done);
				});
			});
		}
	};
	Mongo.Session = function(mongodb, db, collection, onDone){
		this.db = db;
		this.mongodb = mongodb;
		
		this.collection  = collection;
		this.onDone      = onDone;
		this.isDone      = true;
		this.id          = '?';
		
		this.__alternate = db.collectionName;
	};
	Mongo.Session.prototype = {
		findById  : function(id, onResponse, onError){
			this.find({example:{_id: new ObjectID(id)}}, onResponse, onError);
		}
		,_safe : function(){
			var session = this;
			var args = arguments;
			session.isDone = false;
			if( !session.collection ){
				this.mongodb.execute(this.db, function(id, collection, onDone){
					session.id = id;
					session.collection = collection;
					session.onDone = onDone;
					session._run.apply(session, args);
				})
			}else{
				session._run.apply(session, args);
			}
		}
		,_run : function(method, args){
			this.db._log.info('[conn:${session.id}] - ${name}()', {name:method, db:this.mongodb, collection:this.db, session:this});
			this.collection[method].apply(this.collection, args);
		}
		,_responseHandler : function(onResponse, onError){
			var session = this;
			return (function(err, docs){
				session.isDone = true;
				if( err && onError ) 
					onError(err, session);
				else if( onResponse ){
					onResponse(docs ? docs : err, session);
				}
				session.close();
			});
		}
		,close : function(){
			if( this.isDone ) 
				this.onDone();
		}
		,find : function(query, onResponse, onError){
			var session = this;
			var onCursor = function(err, cursor){
				cursor.toArray(session._responseHandler(onResponse, onError));
			}
			this._safe('find', [query.example, query.fields, query.options, onCursor]);
		}, save : function(document, onResponse, onError){
			this._safe('save', [document, {}, this._responseHandler(onResponse, onError)]);
		}, update : function(criteria, document, options, onResponse, onError){
			this._safe('update', [criteria, document, options, this._responseHandler(onResponse, onError)]);
		}, insert : function(document, options, onResponse, onError){
			this._safe('insert', [criteria, document, options, this._responseHandler(onResponse, onError)]);
		}, remove : function(criteria, options, onResponse, onError){
			this._safe('remove', [criteria, options, this._responseHandler(onResponse, onError)]);
		}	
	};
	
	Mongo.DB = function(args){
		this.parent = args.parent;
		this.collectionName = args.collectionName;
		this.username = args.username;
		this.password = args.password;
		this.__serviceName = args.collectionName;
		this._log = logFactory.create('DB.' + this.collectionName);
	}
	Mongo.DB.prototype = {
		_createSession : function(collection, onDone){
			return new Mongo.Session(this.parent, this, collection, onDone);
		},
        findById : function(id, onResponse, onError){
        	this._createSession().findById(id, onResponse, onError);
        },
		find : function(query, onResponse, onError){
			this._createSession().find(query, onResponse, onError);
		},
        save : function(obj, onResponse, onError){
        	if( typeof obj._id == 'string' ){
        		obj._id = new ObjectID(obj._id);
        	}
        	this._createSession().save(obj, onResponse, onError);
        },
        update : function(criteria, document, options, onResponse, onError){
        	if(typeof options === 'function') onResponse = options;
            this._createSession().update(criteria, document, options, onResponse, onError);
        },
        insert : function(document, options, onResponse, onError){
        	if(typeof options === 'function') onResponse = options;
        	this._createSession().insert(document, options, onResponse, onError);
        },
        remove : function(criteria, options, onResponse, onError){
        	if(typeof options === 'function'){
        		onResponse = options;
        		options = null;
        	}
        	this._createSession().remove(criteria, options, onResponse, onError);
        }
	};
	exports.create = function(host, port, dbname){
		return new Mongo(host,port,dbname);
	};
})();